// /////////////////////////////////////////////////////////////////////////////
//
// Name:            EventManager.cpp
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           The Event Manager is the central interface point to all
//                  event related functions of this program.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

// Include files
#include "EventManager.h"


// EventManager class
// EventManager constructor.
EventManager::EventManager()
{
    init();
}

// EventManager deconstructor.
EventManager::~EventManager()
{
    clear();
}

// Initialises the Event Manager.
void EventManager::init()
{
    generateKeyMap();

    // Create the event group for the Event Manager. This group should take care of core events.
    createEventGroup("EventManager");
    std::map<std::string, EventSlot> &eventGroup = mEvents["EventManager"];

    // Add mouse events.
    createEventSlot("EventManager", "onMouse");
    createEventSlot("EventManager", "onMouseMoved");
    createEventSlot("EventManager", "onMouseDown#Left");
    createEventSlot("EventManager", "onMouseDown#Right");
    createEventSlot("EventManager", "onMouseDown#Middle");
    createEventSlot("EventManager", "onMouseUp#Left");
    createEventSlot("EventManager", "onMouseUp#Right");
    createEventSlot("EventManager", "onMouseUp#Middle");
    createEventSlot("EventManager", "onMouseWheel");

    // Add event slots to the event group.
    std::string strOnKeyDown = "onKeyDown#";
    std::string strOnKeyUp = "onKeyUp#";

    std::map<EKEY_CODE, std::string>::iterator it;
    for(it = mKeyMap.begin(); it != mKeyMap.end(); it++)
    {
        EventSlot slot1, slot2;
        eventGroup[strOnKeyDown + it->second] = slot1;
        eventGroup[strOnKeyUp + it->second] = slot2;
    }

    createEventSlot("EventManager", "onKey");

    // Add misc. event slots.
    createEventSlot("EventManager", "onGUIEvent");
}

// Clears the Event Manager.
void EventManager::clear()
{
    mEvents.clear();
}

// Generates a string-based keymap, so that keys can be used by the event manager.
void EventManager::generateKeyMap()
{
#define addToKeyMap(x) mKeyMap[x] = #x
    addToKeyMap(KEY_LBUTTON);
    addToKeyMap(KEY_RBUTTON);
    addToKeyMap(KEY_CANCEL);
    addToKeyMap(KEY_MBUTTON);
    addToKeyMap(KEY_XBUTTON1);
    addToKeyMap(KEY_XBUTTON2);
    addToKeyMap(KEY_BACK);
    addToKeyMap(KEY_TAB);
    addToKeyMap(KEY_CLEAR);
    addToKeyMap(KEY_RETURN);
    addToKeyMap(KEY_SHIFT);
    addToKeyMap(KEY_CONTROL);
    addToKeyMap(KEY_MENU);
    addToKeyMap(KEY_PAUSE);
    addToKeyMap(KEY_CAPITAL);
    addToKeyMap(KEY_KANA);
    addToKeyMap(KEY_HANGUEL);
    addToKeyMap(KEY_HANGUL);
    addToKeyMap(KEY_JUNJA);
    addToKeyMap(KEY_FINAL);
    addToKeyMap(KEY_HANJA);
    addToKeyMap(KEY_KANJI);
    addToKeyMap(KEY_ESCAPE);
    addToKeyMap(KEY_CONVERT);
    addToKeyMap(KEY_NONCONVERT);
    addToKeyMap(KEY_ACCEPT);
    addToKeyMap(KEY_MODECHANGE);
    addToKeyMap(KEY_SPACE);
    addToKeyMap(KEY_PRIOR);
    addToKeyMap(KEY_NEXT);
    addToKeyMap(KEY_END);
    addToKeyMap(KEY_HOME);
    addToKeyMap(KEY_LEFT);
    addToKeyMap(KEY_UP);
    addToKeyMap(KEY_RIGHT);
    addToKeyMap(KEY_DOWN);
    addToKeyMap(KEY_SELECT);
    addToKeyMap(KEY_PRINT);
    addToKeyMap(KEY_EXECUT);
    addToKeyMap(KEY_SNAPSHOT);
    addToKeyMap(KEY_INSERT);
    addToKeyMap(KEY_DELETE);
    addToKeyMap(KEY_HELP);
    addToKeyMap(KEY_KEY_0);
    addToKeyMap(KEY_KEY_1);
    addToKeyMap(KEY_KEY_2);
    addToKeyMap(KEY_KEY_3);
    addToKeyMap(KEY_KEY_4);
    addToKeyMap(KEY_KEY_5);
    addToKeyMap(KEY_KEY_6);
    addToKeyMap(KEY_KEY_7);
    addToKeyMap(KEY_KEY_8);
    addToKeyMap(KEY_KEY_9);
    addToKeyMap(KEY_KEY_A);
    addToKeyMap(KEY_KEY_B);
    addToKeyMap(KEY_KEY_C);
    addToKeyMap(KEY_KEY_D);
    addToKeyMap(KEY_KEY_E);
    addToKeyMap(KEY_KEY_F);
    addToKeyMap(KEY_KEY_G);
    addToKeyMap(KEY_KEY_H);
    addToKeyMap(KEY_KEY_I);
    addToKeyMap(KEY_KEY_J);
    addToKeyMap(KEY_KEY_K);
    addToKeyMap(KEY_KEY_L);
    addToKeyMap(KEY_KEY_M);
    addToKeyMap(KEY_KEY_N);
    addToKeyMap(KEY_KEY_O);
    addToKeyMap(KEY_KEY_P);
    addToKeyMap(KEY_KEY_Q);
    addToKeyMap(KEY_KEY_R);
    addToKeyMap(KEY_KEY_S);
    addToKeyMap(KEY_KEY_T);
    addToKeyMap(KEY_KEY_U);
    addToKeyMap(KEY_KEY_V);
    addToKeyMap(KEY_KEY_W);
    addToKeyMap(KEY_KEY_X);
    addToKeyMap(KEY_KEY_Y);
    addToKeyMap(KEY_KEY_Z);
    addToKeyMap(KEY_LWIN);
    addToKeyMap(KEY_RWIN);
    addToKeyMap(KEY_APPS);
    addToKeyMap(KEY_SLEEP);
    addToKeyMap(KEY_NUMPAD0);
    addToKeyMap(KEY_NUMPAD1);
    addToKeyMap(KEY_NUMPAD2);
    addToKeyMap(KEY_NUMPAD3);
    addToKeyMap(KEY_NUMPAD4);
    addToKeyMap(KEY_NUMPAD5);
    addToKeyMap(KEY_NUMPAD6);
    addToKeyMap(KEY_NUMPAD7);
    addToKeyMap(KEY_NUMPAD8);
    addToKeyMap(KEY_NUMPAD9);
    addToKeyMap(KEY_MULTIPLY);
    addToKeyMap(KEY_ADD);
    addToKeyMap(KEY_SEPARATOR);
    addToKeyMap(KEY_SUBTRACT);
    addToKeyMap(KEY_DECIMAL);
    addToKeyMap(KEY_DIVIDE);
    addToKeyMap(KEY_F1);
    addToKeyMap(KEY_F2);
    addToKeyMap(KEY_F3);
    addToKeyMap(KEY_F4);
    addToKeyMap(KEY_F5);
    addToKeyMap(KEY_F6);
    addToKeyMap(KEY_F7);
    addToKeyMap(KEY_F8);
    addToKeyMap(KEY_F9);
    addToKeyMap(KEY_F10);
    addToKeyMap(KEY_F11);
    addToKeyMap(KEY_F12);
    addToKeyMap(KEY_F13);
    addToKeyMap(KEY_F14);
    addToKeyMap(KEY_F15);
    addToKeyMap(KEY_F16);
    addToKeyMap(KEY_F17);
    addToKeyMap(KEY_F18);
    addToKeyMap(KEY_F19);
    addToKeyMap(KEY_F20);
    addToKeyMap(KEY_F21);
    addToKeyMap(KEY_F22);
    addToKeyMap(KEY_F23);
    addToKeyMap(KEY_F24);
    addToKeyMap(KEY_NUMLOCK);
    addToKeyMap(KEY_SCROLL);
    addToKeyMap(KEY_LSHIFT);
    addToKeyMap(KEY_RSHIFT);
    addToKeyMap(KEY_LCONTROL);
    addToKeyMap(KEY_RCONTROL);
    addToKeyMap(KEY_LMENU);
    addToKeyMap(KEY_RMENU);
    addToKeyMap(KEY_PLUS);
    addToKeyMap(KEY_COMMA);
    addToKeyMap(KEY_MINUS);
    addToKeyMap(KEY_PERIOD);
    addToKeyMap(KEY_ATTN);
    addToKeyMap(KEY_CRSEL);
    addToKeyMap(KEY_EXSEL);
    addToKeyMap(KEY_EREOF);
    addToKeyMap(KEY_PLAY);
    addToKeyMap(KEY_ZOOM);
    addToKeyMap(KEY_PA1);
    addToKeyMap(KEY_OEM_CLEAR);
#undef addToKeyMap
}

// Increases the reference counter.
void EventManager::grab()
{
    // Dummy function.
}

// Decreases the reference counter.
void EventManager::drop()
{
    // Dummy function.
}

// Creates a new event group, a collection of events.
bool EventManager::createEventGroup(const std::string &groupName)
{
    // Check if the group doesn't already exist.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator it;
    it = mEvents.find(groupName);

    if(it != mEvents.end())
       return false;

    // Create a new event group.
    std::map<std::string, EventSlot> group;
    mEvents[groupName] = group;

    return true;
}

// Creates a new event slot in the given event group.
bool EventManager::createEventSlot(const std::string &groupName, const std::string &slotName)
{
    // Get the given event group.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator itGroups;
    itGroups = mEvents.find(groupName);

    if(itGroups == mEvents.end())
      return false;

    std::map<std::string, EventSlot> &group = itGroups->second;

    // Check if the given slot doesnt already exist.
    std::map<std::string, EventSlot>::iterator itSlots = group.find(slotName);

    if(itSlots != group.end())
      return false;

    // Create a new event slot.
    EventSlot slot;
    group[slotName] = slot;

    return true;
}

// Emits an event to the given event slot.
bool EventManager::emitEvent(const std::string &groupName, const std::string &slotName, void *p)
{
    // Get the given event group.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator itGroups;
    itGroups = mEvents.find(groupName);

    if(itGroups == mEvents.end())
      return false;

    std::map<std::string, EventSlot> &group = itGroups->second;

    // Get the given event slot.
    std::map<std::string, EventSlot>::iterator itSlots = group.find(slotName);

    if(itSlots == group.end())
      return false;

    EventSlot &slot = itSlots->second;

    // Emit event.
    slot.emit(p);
    return true;
}

// Checks if the given key is down.
bool EventManager::getIsKeyDown(EKEY_CODE keyCode) const
{
    return mKeyState[keyCode];
}

// Checks if the given key is up.
bool EventManager::getIsKeyUp(EKEY_CODE keyCode) const
{
    return !mKeyState[keyCode];
}

// Returns the X-coordinate of the mouse position.
s32 EventManager::getMouseX() const
{
    return mMousePos.X;
}

// Returns the Y-coordinate of the mouse position.
s32 EventManager::getMouseY() const
{
    return mMousePos.Y;
}

// Returns the position of the mouse as a 2d vector.
const vector2di& EventManager::getMousePos() const
{
    return mMousePos;
}

// Gets the mouse wheel position.
f32 EventManager::getMouseWheel() const
{
    return mMouseWheel;
}


// Removes all event groups from the Event Manager.
void EventManager::removeEventGroups()
{
    // Remove all groups.
    mEvents.clear();
}

// Removes all event slots from the given event group.
bool EventManager::removeEventSlots(const std::string &groupName)
{
    // Get the given event group.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator itGroups;
    itGroups = mEvents.find(groupName);

    if(itGroups == mEvents.end())
      return false;

    std::map<std::string, EventSlot> &group = itGroups->second;

    // Remove all slots.
    group.clear();
    return true;
}

// Removes the given event group from the Event Manager.
bool EventManager::removeEventGroup(const std::string &groupName)
{
    // Get the given event group.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator itGroups;
    itGroups = mEvents.find(groupName);

    if(itGroups == mEvents.end())
      return false;

    // Remove it.
    mEvents.erase(itGroups);
    return true;
}

// Removes the given event slot from the given event group.
bool EventManager::removeEventSlot(const std::string &groupName, const std::string &slotName)
{
    // Get the given event group.
    std::map<std::string, std::map<std::string, EventSlot> >::iterator itGroups;
    itGroups = mEvents.find(groupName);

    if(itGroups == mEvents.end())
      return false;

    std::map<std::string, EventSlot> &group = itGroups->second;

    // Get the given event slot.
    std::map<std::string, EventSlot>::iterator itSlots = group.find(slotName);

    if(itSlots == group.end())
      return false;

    // Remove it.
    group.erase(itSlots);
    return true;
}

// Handles the events sent by the Irrlicht engine.
bool EventManager::OnEvent(const SEvent &event)
{
    // Keyboard event.
    if(event.EventType == EET_KEY_INPUT_EVENT)
    {
        // Store the value in the key map.
        mKeyState[event.KeyInput.Key] = event.KeyInput.PressedDown;

        // Emit key event.
        mEvents["EventManager"]["onKey"].emit((void*)&event.KeyInput);

        // Emit the event.
        std::string slotName;

        if(event.KeyInput.PressedDown)
          slotName = "onKeyDown#";

        else slotName = "onKeyUp#";

        mEvents["EventManager"][slotName + mKeyMap[event.KeyInput.Key]].emit((void*)&event.KeyInput);
    }

    // Mouse event.
    if(event.EventType == EET_MOUSE_INPUT_EVENT)
    {
        switch(event.MouseInput.Event)
        {
           // Emit mouse event.
           mEvents["EventManager"]["onMouse"].emit((void*)&event.MouseInput);

           // Emit specific event.
           case EMIE_MOUSE_MOVED:   // Did the mouse move?
              mMousePos.X = event.MouseInput.X;
              mMousePos.Y = event.MouseInput.Y;
              mEvents["EventManager"]["onMouseMoved"].emit((void*)&event.MouseInput);
              break;

           case EMIE_LMOUSE_PRESSED_DOWN:   // Left mouse button was pressed down.
              mEvents["EventManager"]["onMouseDown#Left"].emit((void*)&event.MouseInput);
              break;

           case EMIE_RMOUSE_PRESSED_DOWN:   // Right mouse button was pressed down.
              mEvents["EventManager"]["onMouseDown#Right"].emit((void*)&event.MouseInput);
              break;

           case EMIE_MMOUSE_PRESSED_DOWN:   // Middle mouse button was pressed down.
              mEvents["EventManager"]["onMouseDown#Middle"].emit((void*)&event.MouseInput);
              break;

           case EMIE_LMOUSE_LEFT_UP:        // Left mouse button was left up.
              mEvents["EventManager"]["onMouseUp#Left"].emit((void*)&event.MouseInput);
              break;

           case EMIE_RMOUSE_LEFT_UP:        // Right mouse button was left up.
              mEvents["EventManager"]["onMouseUp#Right"].emit((void*)&event.MouseInput);
              break;

           case EMIE_MMOUSE_LEFT_UP:        // Middle mouse button was left up.
              mEvents["EventManager"]["onMouseUp#Middle"].emit((void*)&event.MouseInput);
              break;

           case EMIE_MOUSE_WHEEL:           // The mouse wheel was moved.
              mMouseWheel = event.MouseInput.Wheel;
              mEvents["EventManager"]["onMouseWheel"].emit((void*)&event.MouseInput);
              break;

           default: break;
        }
    }

    // GUI event.
    if(event.EventType == EET_GUI_EVENT)
      mEvents["EventManager"]["onGUIEvent"].emit((void*)&event.GUIEvent);

    return false;
}

// End of File.
